#include "sample_svp_nnie_software.h"  // SAMPLE_SVP_NNIE_QUANT_BASE

# include "efficientdet.h"

/* *****************************************************
* 低层函数
******************************************************* */

// 裁剪bbox，防止坐标超出图像
static HI_FLOAT Efficient_Clip(HI_FLOAT value, HI_BOOL is_h){
	HI_FLOAT max;
    if(is_h)
        max = EFFICIENTDET_INPUT_HEIGHT - 1;
    else
        max = EFFICIENTDET_INPUT_WIDTH - 1;
	value = value > max ? max : value;
	value = value < 0 ? 0 : value;
	return value;
}


// 快速排序  6个为一组,按第一个数排序
// 原理: 以最左的数为坐标,将其余数划分到其左右,左大右小. 具体来说先从右开始向左找到一个大数,再从左向右找到一个小数,交换它们,然后继续搜索,直到索引位置重合
static void Efficient_QuickSort_6(HI_FLOAT *nums, HI_S32 left, HI_S32 right){
    // 输入left 和 right 为第一组数和最后一组数,第一个数所在的位置
	if(left>=right) return;
	if(left > EFFICIENTDET_SAVE_MAX_NUM*6) return;
	HI_S32 i=left, j=right; // i=left+1 也 ok
	HI_FLOAT tmp;

    // 从右左相继找大小数,并交换它们,重复这个过程
	while(i<j){ // 一旦重合就跳出
		// 左大右小
		while(nums[j]<=nums[left] && i<j) j -= 6; // 从右往左,找大数
		while(nums[i]>=nums[left] && i<j) i += 6; // 从左往右,找小数

		if(i<j){ // 如果重合了,则不交换
			for(int u=0; u<6; ++u){ // 索引到的左边与右边的数交换
				tmp = nums[i+u];
				nums[i+u] = nums[j+u];
				nums[j+u] = tmp;
			}
		}
	}
	for(HI_S32 u=0; u<6; ++u){
		tmp = nums[i+u];
		nums[i+u] = nums[left+u];
		nums[left+u] = tmp;
	}

	Efficient_QuickSort_6(nums, left, i-6); // 均不包含left位置
	Efficient_QuickSort_6(nums, i+6, right);
}



/* *****************************************************
* 运行函数
******************************************************* */

// NNIE forward 
HI_S32 EfficientDet_NNIE_Forward(SAMPLE_SVP_NNIE_PARAM_S *pstNnieParam, SAMPLE_SVP_NNIE_INPUT_DATA_INDEX_S* pstInputDataIdx,
                                 SAMPLE_SVP_NNIE_PROCESS_SEG_INDEX_S* pstProcSegIdx, HI_BOOL bInstant)
{
    HI_S32 s32Ret = HI_SUCCESS;
    HI_U32 i = 0, j = 0;
    HI_BOOL bFinish = HI_FALSE;
    SVP_NNIE_HANDLE hSvpNnieHandle = 0;
    HI_U32 u32TotalStepNum = 0;

    SAMPLE_COMM_SVP_FlushCache(pstNnieParam->astForwardCtrl[pstProcSegIdx->u32SegIdx].stTskBuf.u64PhyAddr,
        (HI_VOID *) pstNnieParam->astForwardCtrl[pstProcSegIdx->u32SegIdx].stTskBuf.u64VirAddr,
        pstNnieParam->astForwardCtrl[pstProcSegIdx->u32SegIdx].stTskBuf.u32Size); // 清空Tskbuf的cache、或是将Tskbuf的内容输入内存中

    /*set input blob according to node name*/
    // 由于在硬件初始化时，仅分配了目标节点blob的空间，并未将源节点与目标节点对应，该步骤就是让他们对应起来
    if(pstInputDataIdx->u32SegIdx != pstProcSegIdx->u32SegIdx) // 如果输入数据的段 和正要处理的段 id不一样
    {
        // 就在解析的模型里面搜索，将输入段的目标节点与处理段的源节点对应起来
        for(i = 0; i < pstNnieParam->pstModel->astSeg[pstProcSegIdx->u32SegIdx].u16SrcNum; i++) // 遍历当前处理段的所有源节点
        {
            for(j = 0; j < pstNnieParam->pstModel->astSeg[pstInputDataIdx->u32SegIdx].u16DstNum; j++) // 遍历输入段的目标节点
            {
                if(0 == strncmp(pstNnieParam->pstModel->astSeg[pstInputDataIdx->u32SegIdx].astDstNode[j].szName,
                    pstNnieParam->pstModel->astSeg[pstProcSegIdx->u32SegIdx].astSrcNode[i].szName,
                    SVP_NNIE_NODE_NAME_LEN)) // 节点id相等
                {
                    pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astSrc[i] =
                        pstNnieParam->astSegData[pstInputDataIdx->u32SegIdx].astDst[j]; // 处理段源节点 由 输入段目标节点 相同(地址、跨度、blob的hwc、blob类型)
                    break;
                }
            }
            SAMPLE_SVP_CHECK_EXPR_RET((j == pstNnieParam->pstModel->astSeg[pstInputDataIdx->u32SegIdx].u16DstNum), // 未找到源节点所对应的目标节点
                HI_FAILURE,SAMPLE_SVP_ERR_LEVEL_ERROR,"Error,can't find %d-th seg's %d-th src blob!\n",
                pstProcSegIdx->u32SegIdx,i);
        }
    }

    /*NNIE_Forward*/
    s32Ret = HI_MPI_SVP_NNIE_Forward(&hSvpNnieHandle, // handle
        pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astSrc, // 源节点信息
        pstNnieParam->pstModel, pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst, // 目标节点信息
        &pstNnieParam->astForwardCtrl[pstProcSegIdx->u32SegIdx], bInstant); // 控制参数  是否判断完成
    SAMPLE_SVP_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error,HI_MPI_SVP_NNIE_Forward failed!\n");

    // 调用API 查询任务句柄以及对应的某个nnie的id是否完成了工作
    if(bInstant)
    {
        /*Wait NNIE finish*/
        while(HI_ERR_SVP_NNIE_QUERY_TIMEOUT == (s32Ret = HI_MPI_SVP_NNIE_Query(pstNnieParam->astForwardCtrl[pstProcSegIdx->u32SegIdx].enNnieId,
            hSvpNnieHandle, &bFinish, HI_TRUE)))
        {
            usleep(100);
            SAMPLE_SVP_TRACE(SAMPLE_SVP_ERR_LEVEL_INFO,
                "HI_MPI_SVP_NNIE_Query Query timeout!\n"); // 还有超时提示
        }
    }

    bFinish = HI_FALSE; // 清空nnie完成标志位
    // 由于nnie得到的结果存在cache里，需对每一个目标节点刷新cache
    for(i = 0; i < pstNnieParam->astForwardCtrl[pstProcSegIdx->u32SegIdx].u32DstNum; i++) // 遍历处理段的目标节点
    {
        if(SVP_BLOB_TYPE_SEQ_S32 == pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].enType)
        {
            for(j = 0; j < pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u32Num; j++)
            {
                u32TotalStepNum += *((HI_U32*)(pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].unShape.stSeq.u64VirAddrStep)+j);
            }
            SAMPLE_COMM_SVP_FlushCache(pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u64PhyAddr,
                (HI_VOID *) pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u64VirAddr,
                u32TotalStepNum*pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u32Stride);

        }
        else // 节点blob类型为u8等
        {
            SAMPLE_COMM_SVP_FlushCache(pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u64PhyAddr, // 目标节点 物理地址
                (HI_VOID *) pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u64VirAddr,// 虚拟地址
                pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u32Num* // batch * c * h * stride(不是w)
                pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].unShape.stWhc.u32Chn*
                pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].unShape.stWhc.u32Height*
                pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u32Stride);
        }
    }

    return s32Ret;
}



// 软件解码: 位置解算 + nms
HI_S32 EfficientDet_Soft_ParamInit(SVP_SOFT_EfficientDet *pstSoftParam)
{
    HI_S32 s32Ret = HI_SUCCESS;
    HI_U64 u64PhyAddr = 0;
    HI_U8* pu8VirAddr = NULL;

    // 计算内存大小
    pstSoftParam->ps32ResultMem_beforeNMS.u32Size = SAMPLE_SVP_NNIE_ALIGN16(EFFICIENTDET_SAVE_MAX_NUM * 6 * sizeof(HI_FLOAT)); // NMS前的结果存储
    pstSoftParam->is_suppression.u32Size = SAMPLE_SVP_NNIE_ALIGN16(EFFICIENTDET_SAVE_MAX_NUM * sizeof(HI_BOOL));
    pstSoftParam->ps32FinalBbox.u32Size = SAMPLE_SVP_NNIE_ALIGN16(EFFICIENTDET_OUT_MAX_NUM * 6 * sizeof(HI_FLOAT));
    HI_U32 u32TotalSize = pstSoftParam->ps32ResultMem_beforeNMS.u32Size + pstSoftParam->is_suppression.u32Size + pstSoftParam->ps32FinalBbox.u32Size;

    // 申请内存
    s32Ret = SAMPLE_COMM_SVP_MallocCached("EfficientDet", NULL, (HI_U64*)&u64PhyAddr, (void**)&pu8VirAddr, u32TotalSize);
    SAMPLE_SVP_CHECK_EXPR_RET(HI_SUCCESS != s32Ret, HI_FAILURE,SAMPLE_SVP_ERR_LEVEL_ERROR,"Error,Malloc memory failed!\n");
    memset(pu8VirAddr, 0, u32TotalSize);
    SAMPLE_COMM_SVP_FlushCache(u64PhyAddr, (void*)pu8VirAddr, u32TotalSize);

    //分配内存
    pstSoftParam->ps32ResultMem_beforeNMS.u64PhyAddr = u64PhyAddr;  // NMS前检测结果存储空间
    pstSoftParam->ps32ResultMem_beforeNMS.u64VirAddr = (HI_U64)(pu8VirAddr);
    pstSoftParam->is_suppression.u64PhyAddr = u64PhyAddr + pstSoftParam->ps32ResultMem_beforeNMS.u32Size;  // NMS抑制标志位
    pstSoftParam->is_suppression.u64VirAddr = (HI_U64)(pu8VirAddr + pstSoftParam->ps32ResultMem_beforeNMS.u32Size);
    pstSoftParam->ps32FinalBbox.u64PhyAddr = u64PhyAddr + pstSoftParam->is_suppression.u32Size + 
                                             pstSoftParam->ps32ResultMem_beforeNMS.u32Size;  // NMS后检测结果存储空间
    pstSoftParam->ps32FinalBbox.u64VirAddr = (HI_U64)(pu8VirAddr + pstSoftParam->is_suppression.u32Size + 
                                             pstSoftParam->ps32ResultMem_beforeNMS.u32Size);

    pstSoftParam->pu8VirAddr = pu8VirAddr;
    pstSoftParam->u32TotalSize = u32TotalSize;
    return s32Ret;
}



// 软件解码: 位置解算 + nms
HI_S32 EfficientDet_Soft(SAMPLE_SVP_NNIE_PARAM_S *pstDetParam, SVP_SOFT_EfficientDet *pstSoftParam)
{
    HI_S32 s32Ret = HI_SUCCESS;
    memset(pstSoftParam->pu8VirAddr, 0, pstSoftParam->u32TotalSize);  // 将软件内存清0,主要是对标志位清0

	// ***************       anchor 参数设置       ***************
	HI_S32 image_stride[EFFICIENTDET_LAYER_NUM] = {8, 16, 32, 64, 128}; // 步长
	HI_S32 anchor_size[EFFICIENTDET_LAYER_NUM] = EFFICIENTDET_ANCHOR_INFO; // {16, 32, 64, 128, 256}; 

	// ***************       获取 NNIE 输出       ***************
	for (HI_U32 i = 0; i < EFFICIENTDET_LAYER_NUM * 2; i++) { // chw
		if(i < EFFICIENTDET_LAYER_NUM)
			pstSoftParam->nnie_data_reg[i] = (HI_S32*)pstDetParam->astSegData[0].astDst[i].u64VirAddr; // loc
		else
			pstSoftParam->nnie_data_cls[i - EFFICIENTDET_LAYER_NUM] = (HI_S32*)pstDetParam->astSegData[0].astDst[i].u64VirAddr; // conf
	} 

	// ***************       获取 NNIE输出 形状       ***************
	for (HI_U32 i = 0; i < EFFICIENTDET_LAYER_NUM; i++){
		pstSoftParam->features_w[i] = pstDetParam->astSegData[0].astDst[i].unShape.stWhc.u32Width;
		pstSoftParam->features_h[i] = pstDetParam->astSegData[0].astDst[i].unShape.stWhc.u32Height;
		pstSoftParam->features_reg_c[i] = pstDetParam->astSegData[0].astDst[i].unShape.stWhc.u32Chn;
		pstSoftParam->feature_cls_c[i] = pstDetParam->astSegData[0].astDst[i+EFFICIENTDET_LAYER_NUM].unShape.stWhc.u32Chn;
		pstSoftParam->feature_stride[i] = pstDetParam->astSegData[0].astDst[i].u32Stride;
		// printf("%dth feature shape: w: %d   h: %d   c_loc: %d   c_conf: %d   s: %d\n", i, pstSoftParam->features_w[i], pstSoftParam->features_h[i], 
        //         pstSoftParam->features_reg_c[i], pstSoftParam->feature_cls_c[i], pstSoftParam->feature_stride[i]);
	}
    // 确保blob通道数无误  如果IoU/conf分支存在,可将下列注释掉,并通过修改define选择是否使用该分支
    if((EFFICIENTDET_PREDICT_CONF && pstSoftParam->feature_cls_c[0] != EFFICIENTDET_CLASS_NUM+1) ||  
       (!EFFICIENTDET_PREDICT_CONF && pstSoftParam->feature_cls_c[0] != EFFICIENTDET_CLASS_NUM)){
        printf("Error: cls channel num is not right\n");
        return -1;
    }
    if((EFFICIENTDET_PREDICT_IOU && pstSoftParam->features_reg_c[0] != 4+1) ||  
       (!EFFICIENTDET_PREDICT_IOU && pstSoftParam->features_reg_c[0] != 4)){
        printf("Error: reg channel num is not right\n");
        return -1;
    }  
     
	// ***************       得分筛选 + 坐标解算       ***************
    HI_FLOAT cls_thresh = -log(1/EFFICIENTDET_CLASS_THRESHOLD - 1);  // 计算得分阈值
    // printf("cls_thresh: %.4f\n", cls_thresh);
    pstSoftParam->det_num_before_nms = 0;
	for (HI_U32 i = 0; i < EFFICIENTDET_LAYER_NUM; i++){
		// printf("i: %d\n", i);
		HI_S32* ps32InputData = (HI_S32*)pstSoftParam->nnie_data_cls[i];
		HI_S32* ps32InputLoc = (HI_S32*)pstSoftParam->nnie_data_reg[i];
		for(HI_U32 h=0; h<pstSoftParam->features_h[i]; h++){ // h
            // printf("  h: %d\n", h);
			for(HI_U32 w=0; w<pstSoftParam->features_w[i]; w++){ // w
                // printf("    w: %d\n", w);
                HI_FLOAT score = -9999;
                HI_U32 dclass = 0;
                if(EFFICIENTDET_PREDICT_CONF){  // 若包含置信度分支
                    HI_U32 index = EFFICIENTDET_CLASS_NUM * pstSoftParam->features_h[i] * (pstSoftParam->feature_stride[i]/4) +
                                   h * (pstSoftParam->feature_stride[i]/4) + w; // chw  除以4是因为stride的单位是byte而非个数,1个数占4byte
                    HI_S32* InputSingleData = ps32InputData + index;
                    score = InputSingleData[0]*1.0f / SAMPLE_SVP_NNIE_QUANT_BASE;
                }
                else{ // 无置信度分支  通过类别来获取置信度
                    for(HI_U32 c=0; c<EFFICIENTDET_CLASS_NUM; c++){
                        HI_U32 index = c * pstSoftParam->feature_stride[i] * (pstSoftParam->features_h[i]/4) + 
                                       h * (pstSoftParam->feature_stride[i]/4) + w; // chw
                        HI_S32* InputSingleData = ps32InputData + index;
                        HI_FLOAT confident = InputSingleData[0]*1.0f / SAMPLE_SVP_NNIE_QUANT_BASE;
                        if(confident > score){
                            score = confident;
                            dclass = c;
                        }
                    }
                }
                
                // 对得分高于阈值的anchor解码 存储为[score class xyxh]
                if(score > cls_thresh){
                	HI_FLOAT delta[4]; // xywh
                    HI_FLOAT preIoU = 1;  // IoU预测分支值
                    // printf("  w:%d  h:%d  score:%.2f  conf:%d\n", w, h, score);

                    // 获取位置+IoU预测结果
                	for(HI_U32 l=0; l<5; l++){
                        if(!EFFICIENTDET_PREDICT_IOU && l == 4)  // 没分支则跳过第5个通道
                            break;
                		HI_U32 index = l * pstSoftParam->features_h[i] * (pstSoftParam->feature_stride[i]/4) + 
                					   h * (pstSoftParam->feature_stride[i]/4) + w;
                		HI_S32* InputSingleLoc = ps32InputLoc + index;
                        if(l == 4){
                            preIoU = InputSingleLoc[0]*1.0f / SAMPLE_SVP_NNIE_QUANT_BASE;
                            preIoU = 1 / (1 + exp(-preIoU));
                            // printf("preIoU: %.4f\n", preIoU);
                        }
                        else
                            delta[l] = InputSingleLoc[0]*1.0f / SAMPLE_SVP_NNIE_QUANT_BASE;
                	}

                    // anchor计算
                    HI_FLOAT anchor_w = anchor_size[i];
                    HI_FLOAT anchor_h = anchor_size[i];
                    HI_FLOAT anchor_x = image_stride[i] * (w + 0.5f);
                    HI_FLOAT anchor_y = image_stride[i] * (h + 0.5f);
                    // 解码位置预测
                	delta[0] *= 0.1f; delta[1] *= 0.1f;
                	delta[2] *= 0.2f; delta[3] *= 0.2f;
                	HI_FLOAT bbox_x = anchor_x + delta[0] * anchor_w;
                	HI_FLOAT bbox_y = anchor_y + delta[1] * anchor_h;
                	HI_FLOAT bbox_w = exp(delta[2]) * anchor_w;
                	HI_FLOAT bbox_h = exp(delta[3]) * anchor_h;
 
                    // 计算类别+得分
                    HI_FLOAT org_score = score;
                    if(EFFICIENTDET_PREDICT_CONF){ // 置信度分支需单独计算预测种类和类别得分
                        score = -9999;
                        for(HI_U32 c=0; c<EFFICIENTDET_CLASS_NUM; c++){
                            HI_U32 index = c * pstSoftParam->feature_stride[i] * (pstSoftParam->features_h[i]/4) + 
                                        h * (pstSoftParam->feature_stride[i]/4) + w; // chw
                            HI_S32* InputSingleData = ps32InputData + index;
                            HI_FLOAT confident = InputSingleData[0]*1.0f / SAMPLE_SVP_NNIE_QUANT_BASE;
                            if(confident > score){
                                score = confident;
                                dclass = c;
                            }
                        }
                    }
                    score = 1 / (1 + exp(-score)); // 类别得分
                    score = score * preIoU; // 最终排序用的得分,由(类别得分*IoU预测得分)组成
                	// printf("   w: %d   h: %d   org_score: %.3f   new_score: %.3f   cls: %d   num: %d   bx: %.3f   by: %.3f   bw: %.3f   bh: %.3f\n", 
                    //         w, h, org_score, score, dclass, pstSoftParam->det_num_before_nms, bbox_x, bbox_y, bbox_w, bbox_h);

                    // 检测数量不可超出上限
                	if(pstSoftParam->det_num_before_nms >= EFFICIENTDET_SAVE_MAX_NUM){
                		printf("Warning! det_num_before_nms out of memory\n");
                		break;
                	}

                    HI_FLOAT* ps32BboxResultTmp = (HI_FLOAT*)pstSoftParam->ps32ResultMem_beforeNMS.u64VirAddr;
                	ps32BboxResultTmp[pstSoftParam->det_num_before_nms*6] = score;
                	ps32BboxResultTmp[pstSoftParam->det_num_before_nms*6 + 1] = dclass;
                	ps32BboxResultTmp[pstSoftParam->det_num_before_nms*6 + 2] = Efficient_Clip(bbox_x - bbox_w/2, 0); // x
                	ps32BboxResultTmp[pstSoftParam->det_num_before_nms*6 + 3] = Efficient_Clip(bbox_y - bbox_h/2, 1); // y
                	ps32BboxResultTmp[pstSoftParam->det_num_before_nms*6 + 4] = Efficient_Clip(bbox_x + bbox_w/2, 0); // x
                	ps32BboxResultTmp[pstSoftParam->det_num_before_nms*6 + 5] = Efficient_Clip(bbox_y + bbox_h/2, 1); // y

                	pstSoftParam->det_num_before_nms ++;
                }
				
			} // w
		} // h
	} // stage
    // printf("\ndetect num: %d    before sort score: \n", pstSoftParam->det_num_before_nms);
	// for(HI_U32 ii=0; ii<pstSoftParam->det_num_before_nms; ++ii)
	// 	printf("ii: %d   score: %.3f\n", ii, ((HI_FLOAT*)pstSoftParam->ps32ResultMem_beforeNMS.u64VirAddr)[ii*6]);
    

    // ***************       按得分排序       ***************
	Efficient_QuickSort_6((HI_FLOAT *)pstSoftParam->ps32ResultMem_beforeNMS.u64VirAddr, 0, pstSoftParam->det_num_before_nms*6-6); // sort
	// printf("\nhas %d detect result, score sort:\n", pstSoftParam->det_num_before_nms);
	// for(HI_U32 ii=0; ii<pstSoftParam->det_num_before_nms; ++ii)
	// 	printf("%.3f\n", ((HI_FLOAT*)pstSoftParam->ps32ResultMem_beforeNMS.u64VirAddr)[ii*6]);


	// ***************       NMS for each class       ***************
	pstSoftParam->final_detection_num = 0;
    HI_FLOAT* all_result = (HI_FLOAT*)pstSoftParam->ps32ResultMem_beforeNMS.u64VirAddr; // [score class x1 y1 x2 y2]
    HI_BOOL* is_suppression = (HI_BOOL*)pstSoftParam->is_suppression.u64VirAddr;
	for(HI_S32 ii=0; ii<EFFICIENTDET_CLASS_NUM; ii++){ // 每个类别进行NMS
		// printf(" ii class: %d\n", ii);
		for(HI_U32 jj=0; jj<pstSoftParam->det_num_before_nms; ++jj){ // 进行一次NMS
			if(all_result[jj*6 + 1] != ii || is_suppression[jj] == HI_TRUE) // 如果不是这个类别 或 被抑制， 则跳过
				continue;

			// printf("  jj: %d\n", jj);
			is_suppression[jj] = HI_TRUE;
			for(HI_U32 ll=0; ll<6; ++ll)  // 保存该结果
                ((HI_FLOAT*)pstSoftParam->ps32FinalBbox.u64VirAddr)[pstSoftParam->final_detection_num*6 + ll] = all_result[jj*6 + ll];
			pstSoftParam->final_detection_num ++;

			for(HI_U32 kk=jj+1; kk<pstSoftParam->det_num_before_nms; ++kk){ // 对后续结果进行抑制
				if(all_result[kk*6 + 1] != ii || is_suppression[kk] == HI_TRUE)
					continue;
				// printf(" ii: %d jj: %d kk: %d\n", ii, jj, kk);

                // box1
				HI_FLOAT xj1 = all_result[jj*6 + 2]; HI_FLOAT yj1 = all_result[jj*6 + 3];
				HI_FLOAT xj2 = all_result[jj*6 + 4]; HI_FLOAT yj2 = all_result[jj*6 + 5];
                // box2
				HI_FLOAT xk1 = all_result[kk*6 + 2]; HI_FLOAT yk1 = all_result[kk*6 + 3];
				HI_FLOAT xk2 = all_result[kk*6 + 4]; HI_FLOAT yk2 = all_result[kk*6 + 5];

                // 计算IoU
				HI_FLOAT overlap_top_left_x = xj1 > xk1 ? xj1 : xk1; // max
				HI_FLOAT overlap_top_left_y = yj1 > yk1 ? yj1 : yk1;
				HI_FLOAT overlap_bot_right_x = xj2 > xk2 ? xk2 : xj2; // min
				HI_FLOAT overlap_bot_right_y = yj2 > yk2 ? yk2 : yj2;
				HI_FLOAT overlap_area;
				if((overlap_bot_right_x - overlap_top_left_x < 0) || (overlap_bot_right_y - overlap_top_left_y < 0))
					overlap_area = 0;
				else
					overlap_area = (overlap_bot_right_x - overlap_top_left_x) * (overlap_bot_right_y - overlap_top_left_y);
				HI_FLOAT union_area = (yk2 - yk1)*(xk2 - xk1) + (yj2 - yj1)*(xj2 - xj1) - overlap_area;
				HI_FLOAT IoU = overlap_area / (union_area+0.0001f);

                // DIoU部分计算
                // bbox、GT中心点欧氏距离的平方  DIoU部分
                HI_FLOAT bboxj_ctr_x = (xj1 + xj2) / 2; HI_FLOAT bboxj_ctr_y = (yj1 + yj2) / 2;
                HI_FLOAT bboxk_ctr_x = (xk1 + xk2) / 2; HI_FLOAT bboxk_ctr_y = (yk1 + yk2) / 2;
                HI_FLOAT p2 = pow(bboxj_ctr_x - bboxk_ctr_x, 2) + pow(bboxj_ctr_y - bboxk_ctr_y, 2);
                // 最小包围box对角线欧氏距离的平方  DIoU部分
                HI_FLOAT c2 = pow(overlap_bot_right_x - overlap_top_left_x, 2) + pow(overlap_bot_right_y - overlap_top_left_y, 2);
                // DIoU损失 = IoU - (ρ(b, bgt) / c) ^ 2 
                HI_FLOAT DIoU = IoU - p2 / c2;


				// printf("  over: %.3f  union: %.3f  IoU: %.3f\n", overlap_area, union_area, IoU);
				if(DIoU > EFFICIENTDET_NMS_IOU_THRESHOLD){
					is_suppression[kk] = HI_TRUE;
					// printf("   is suppression\n");
				}
			}
		}
	} 

    return s32Ret;
}
